package com.harmontronics.hanma.paas.paas.service;

import cn.hutool.json.JSONObject;
import com.harmontronics.hanma.paas.paas.common.PageBase;
import com.harmontronics.hanma.paas.paas.consts.BaseI18nKeyEnum;
import com.harmontronics.hanma.paas.paas.consts.CommonConstants;
import com.harmontronics.hanma.paas.paas.consts.NumberEnum;
import com.harmontronics.hanma.paas.paas.domain.tenant.entity.HmTenant;
import com.harmontronics.hanma.paas.paas.domain.tenant.entity.HmTenantUser;
import com.harmontronics.hanma.paas.paas.domain.tenant.entity.valueobject.I18nKeyEnum;
import com.harmontronics.hanma.paas.paas.domain.tenant.service.HmTenantService;
import com.harmontronics.hanma.paas.paas.exception.ErrorKeyException;
import com.harmontronics.hanma.paas.paas.token.TenantUserTokenKit;
import com.harmontronics.hanma.paas.paas.token.UserInfo;
import com.harmontronics.hanma.paas.paas.utils.*;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import com.harmontronics.hanma.paas.paas.domain.tenant.service.HmTenantUserService;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;


@Service("hmTenantUserAppService")
@Slf4j
@ConditionalOnProperty(prefix = "mos.oss",name="enabled",havingValue = "false")
public class HmTenantUserAppService extends HmTenantUserService implements IHmTenantUserService{

    @Autowired
    private HmPaasTokenService hmPaasTokenService;

    @Autowired
    private HmTenantService hmTenantService;

    @Autowired
    private TenantUserTokenKit tenantUserTokenKit;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    /**
     * 登录
     *
     * @param hmTenantUser
     * @return
     */
    @Override
    public String login(HmTenantUser hmTenantUser) {
        //查询是否有密码错误缓存记录
        JSONObject pwdObj = hmPaasTokenService.getPwdErrorNum(hmTenantUser.getUserName());
        Integer num = 0;
        if (pwdObj != null) {
            num = pwdObj.getInt(CommonConstants.PWD_ERROR_NUM);
            if (num >= NumberEnum.FIVE.getNum()) {
                throw new ErrorKeyException(I18nKeyEnum.ACCOUNT_PWD_LOCK_ERROR, Arrays.asList(pwdObj.getStr(CommonConstants.PWD_LOCK_BEGIN_TIME), NumberEnum.FIVE.getNumStr()));
            }
        }
        //获取用户访问的域名
        String suffix = RequestUtils.getDomainSuffix();
        if (StringUtils.isEmpty(suffix)) {
            log.warn("租户用户登录-访问域名错误", suffix);
            throw new ErrorKeyException(I18nKeyEnum.REQUEST_PARAM_ERROR);
        }
        //通过域名后缀查询租户
        HmTenant hmTenant = hmTenantService.getByDomain(suffix);
        if (hmTenant == null) {
            log.warn("租户用户登录-访问域名错误,没有查询到符合的租户", suffix);
            throw new ErrorKeyException(I18nKeyEnum.TENANT_NOT_EXIST);
        }
        if (hmTenant.getStatus() == NumberEnum.ONE.getNum()) {
            throw new ErrorKeyException(I18nKeyEnum.TENANT_LOCKED);
        }
        HmTenantUser user = super.getUserByName(hmTenant.getId(), hmTenantUser.getUserName());
        if (user == null) {
            throw new ErrorKeyException(I18nKeyEnum.ACCOUNT_OR_PASSWORD_ERROR);
        }
        //判断状态是否被禁用
        if (user.getStatus() == NumberEnum.ONE.getNum()) {
            log.warn("租户用户登录-用户被禁用， userId：" + hmTenantUser.getId());
            throw new ErrorKeyException(I18nKeyEnum.ACCOUNT_FORBIDDEN);
        }
        //验证密码
        if (!PasswordUtil.isEqualMd5Password(
                hmTenantUser.getPwd(), user.getSalt(), user.getPwd())
                || user.getStatus() == NumberEnum.ONE.getNum()
                || user.getDeleted() == NumberEnum.ONE.getNum()) {
            //记录错误次数
            hmPaasTokenService.setPwdErrorNum(hmTenantUser.getUserName(), ++num);
            if (num >= NumberEnum.FIVE.getNum()) {
                throw new ErrorKeyException(I18nKeyEnum.ACCOUNT_PWD_LOCK_ERROR, Arrays.asList(pwdObj.getStr(CommonConstants.PWD_LOCK_BEGIN_TIME), NumberEnum.FIVE.getNumStr()));
            }
            throw new ErrorKeyException(I18nKeyEnum.ACCOUNT_OR_PASSWORD_ERROR);
        }
        return loginWithUser(user, hmTenant);
    }

    private String loginWithUser(HmTenantUser user, HmTenant hmTenant) {

        String hashKey = tenantUserTokenKit.getTokenHashKey(user.getId());

        HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash();
        Map<String, String> dataMap = hashOperations.entries(hashKey);
        String cipherKey = tenantUserTokenKit.makeKey(user.getId());
        if (CollectionUtils.isEmpty(dataMap)) {
            Map<String, String> hashData = new HashMap<>();
            hashData.put(UserInfo.HASH_KEY_USER_ID, user.getId().toString());
            hashData.put(UserInfo.HASH_KEY_ADMIN, user.getIsAdmin() == null ? "0" : user.getIsAdmin().toString());
            hashData.put(UserInfo.HASH_KEY_TENANT_ID, user.getTenantId().toString());
            hashData.put(UserInfo.HASH_KEY_USER_NAME, user.getUserName());
            hashData.put(UserInfo.HASH_KEY_TENANT_NAME,hmTenant.getTenantName());

            LocalDateTime expiredTime = LocalDateTime.now().plusHours(1);
            long expiredMilli = expiredTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            hashData.put("KEY_" + cipherKey, String.valueOf(expiredMilli));
            hashOperations.putAll(hashKey, hashData);
            
        } else {
            for (Map.Entry<String, String> entry : dataMap.entrySet()) {
                if (entry.getKey().startsWith("KEY_")) {
                    hashOperations.put(hashKey, entry.getKey(), "0");
                }
            }
            LocalDateTime expiredTime = LocalDateTime.now().plusHours(1);
            long expiredMilli = expiredTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            hashOperations.put(hashKey, "KEY_" + cipherKey, String.valueOf(expiredMilli));
        }
        stringRedisTemplate.expire(hashKey, 2, TimeUnit.HOURS);
        return cipherKey;
    }

    @Override
    public UserInfo getUserByToken(){
        return tenantUserTokenKit.getUserInfo();
    }

    @Override
    public void logout(){
        tenantUserTokenKit.delete();
    }

    @Override
    public void isAdmin(HmTenantUser hmTenantUser){
        UserInfo userInfo = tenantUserTokenKit.getUserInfo();
        if (!userInfo.isAdmin() && !hmTenantUser.getTenantId().equals(userInfo.getTenantId())) {
            throw new ErrorKeyException(BaseI18nKeyEnum.REQUEST_INVALID);
        }
    }

}